home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
MIDICraft's MIDINET CD-ROM
/
MIDICraft's MIDINET CD-ROM.iso
/
DOSUTILS
/
MIDIFIX.ZIP
/
MIDIFIX.CPP
next >
Wrap
C/C++ Source or Header
|
1997-01-11
|
16KB
|
742 lines
// midifix v1.3 written by Günter Nagler 1995 (gnagler@ihm.tu-graz.ac.at)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#ifdef __MSDOS__
#define READWRITE_BINARY "r+b"
#define CREATE_READWRITE_BINARY "w+b"
#define WRITE_BINARY "wb"
#define READ_BINARY "rb"
#define STRICMP stricmp
#else
#define READWRITE_BINARY "r+"
#define CREATE_READWRITE_BINARY "w+"
#define WRITE_BINARY "w"
#define READ_BINARY "r"
#define STRICMP strcmp
#endif
// uncomment this define if you want to fix channel errors
//#define FIX_CHANNELERROR
#define DEFAULT_OUTPUTNAME "midifix.mid"
#define MAXEVENTSIZE 1024
long filesize = 0;
char* name = 0;
char* outputname = 0;
char* extramidi = "extra%d.mid";
int version = 0;
int tracksonly = 0;
void copybytes(FILE* df, FILE* sf, long n)
{
while (n-- > 0)
{
int c = fgetc(sf);
if (c < 0)
{
perror(name);
exit(1);
}
else
fputc(c, df);
}
}
long getlong(FILE* f)
{
unsigned char v[4];
long len;
if (fread(v, 1, 4, f) != 4)
return -1;
len = v[0];
len = (len << 8) + v[1];
len = (len << 8) + v[2];
len = (len << 8) + v[3];
return len;
}
void putlong(FILE* f, long len)
{
unsigned char v[4];
v[3] = len & 0xff; len >>= 8;
v[2] = len & 0xff; len >>= 8;
v[1] = len & 0xff; len >>= 8;
v[0] = len & 0xff;
if (fwrite(v, 1, 4, f) != 4)
{
perror(name);
exit(1);
}
}
unsigned int getword(FILE* f)
{
unsigned char v[2];
unsigned int len = 0;
if (fread(v, 1, 2, f) == 2)
{
len = v[0];
len = (len << 8) + v[1];
}
return len;
}
void putword(FILE* f, int len)
{
unsigned char v[2];
v[1] = len & 0xff; len >>= 8;
v[0] = len & 0xff;
if (fwrite(v, 1, 2, f) != 2)
{
perror(name);
exit(1);
}
}
long findbytes(FILE* f, unsigned char *s, int len)
{
long oldpos = ftell(f);
long pos = -1, markpos;
int c, matchlen;
if (s && len > 0)
{
while ((c = fgetc(f)) != EOF)
{
if (c == *s)
{
markpos = ftell(f);
matchlen = 1;
while (matchlen < len)
{
c = fgetc(f);
if (c == EOF)
break;
if (c == s[matchlen])
matchlen++;
else
break;
}
if (matchlen == len)
{
pos = markpos-1;
break;
}
else
fseek(f, markpos, SEEK_SET);
}
}
}
fseek(f, oldpos, SEEK_SET);
return pos;
}
unsigned char mthd[] = "MThd";
unsigned char mtrk[] = "MTrk";
unsigned char snio[] = "SNio";
// find MTrk
long findtrack(FILE* f)
{
return findbytes(f, mtrk, 4);
}
// find header tag
long findmidi(FILE* f)
{
long pos = findbytes(f, mthd, 4);
if (pos < 0)
pos = findbytes(f, snio, 4); // taiwan midi format?
return pos;
}
int IsTag(unsigned char* buf, unsigned char* tag)
{
return memcmp(buf, tag, 4) == 0;
}
int IsHeaderTag(unsigned char* tag)
{
return IsTag(tag, mthd) || IsTag(tag, snio);
}
int IsTrackTag(unsigned char* tag)
{
return IsTag(tag, mtrk);
}
long findtrackend(FILE* f)
{
unsigned char endtrk[] = { 0xff, 0x2f, 0x00 };
long endpos;
endpos = findbytes(f, endtrk, 3);
if (endpos > 0)
return endpos+3;
return endpos;
}
void updatetrackcount(FILE*f, int count)
{
long oldpos = ftell(f);
fseek(f, 10, SEEK_SET);
putword(f, count);
printf("number of tracks updated: %d\n", count);
fseek(f, oldpos, SEEK_SET);
}
int get(FILE* f, long &len, unsigned char* event, int & n, int count)
{
if (n + count > MAXEVENTSIZE)
{
fprintf(stderr, "overflow\n");
return 0;
}
while (count > 0 && len > 0)
{
int c = fgetc(f);
if (c < 0)
break;
event[n++] = c;
count--;
len--;
}
if (count > 0)
{
printf( "removing incomplete midi command at %08lX\n", ftell(f)-n);
return 0;
}
return 1;
}
int getdelta(FILE* f, long &len, unsigned char* event, int & n, long &value)
{
value = 0;
for (int i = 0; i < 4; i++)
{
if (!get(f, len, event, n, 1))
return 0;
value = (value << 7) + (event[n-1] & 0x7f);
if ((event[n-1] & 0x80) == 0)
break;
}
return 1;
}
#define NOTCHANGED 0
#define CHANGED 1
#define NOTREPAIRABLE -1
int copytrack(FILE* of, FILE* f, long len)
{
if (tracksonly)
{
// copy track data without checking content
copybytes(of, f, len);
return NOTCHANGED;
}
int lastcode = -1;
int endfound = 0;
static unsigned char event[MAXEVENTSIZE], *cmd, *param;
int n = 0;
int midicode = -1;
int channel = -1;
int channelerror = 0;
int repairedcmd = 0;
while (len > 0)
{
long duration;
n = 0;
// time code
if (!getdelta(f, len, event, n, duration))
goto stoptrack;
cmd = event + n;
if (!get(f, len, event, n, 1))
goto stoptrack;
if (*cmd >= 0x80 || lastcode < 0)
{
midicode = *cmd;
}
else
{
fseek(f, -1, SEEK_CUR);
midicode = lastcode;
n--;
len++;
}
param = event+n;
endfound = 0;
// check command
switch(midicode)
{
case 0xf0: // sysex
{
while (len > 0 && n < sizeof(event) - 1)
{
if (!get(f, len, event, n, 1))
goto stoptrack;
if (event[n-1] == 0xF7) // end of sysex
break;
}
if (event[n-1] != 0xF7)
{
printf( "remove incomplete sysex command\n");
goto stoptrack;
}
}
break;
case 0xf2:
{
if (!get(f, len, event, n, 2))
goto stoptrack;
break;
}
case 0xf3:
if (!get(f, len, event, n, 1))
goto stoptrack;
break;
case 0xf6:
case 0xf8:
case 0xfa:
case 0xfb:
case 0xfc:
case 0xfe:
break;
case 0xff:
{
if (!get(f, len, event, n, 1))
goto stoptrack;
long metalen;
if (!getdelta(f, len, event, n, metalen))
goto stoptrack;
if (len < metalen)
goto stoptrack;
if (n + metalen > MAXEVENTSIZE)
{
fseek(f, -n, SEEK_CUR);
copybytes(of, f, n+metalen);
len -= metalen;
n = 0;
}
else
{
if (!get(f, len, event, n, (int)metalen))
goto stoptrack;
}
switch(param[0])
{
case 0x2F:
endfound = 1;
break;
}
}
break;
default:
{
if (midicode < 0x80)
{
printf( "removing invalid midi command at %08lX\n", ftell(f)-1);
goto stoptrack;
}
if (version == 1 && midicode < 0xf0)
{
if (channel >= 0)
{
if (channel != (midicode & 0x0F))
{
if (!channelerror)
{
printf( "Warning: version 1 track contains different channel commands\n");
#ifdef FIX_CHANNELERROR)
printf( "setting channel to %d\n", channel+1);
#endif
}
#ifdef FIX_CHANNELERROR)
midicode = midicode & 0xF0 + channel; // change channel
#endif
channelerror = 1;
}
}
else
channel = midicode & 0x0F;
}
switch(midicode & 0xF0)
{
case 0x80:
case 0x90:
{
lastcode = midicode;
if (!get(f, len, event, n, 2))
goto stoptrack;
if (param[0] >= 0x80 || param[1] >= 0x80)
{
printf( "removing invalid note command at %08lX\n", ftell(f)-3);
goto stoptrack;
}
}
break;
case 0xA0:
{
if (!get(f, len, event, n, 2))
goto stoptrack;
lastcode = midicode;
}
break;
case 0xB0:
{
if (!get(f, len, event, n, 2))
goto stoptrack;
if (param[1] >= 0x80)
{
printf( "invalid control value used at %08lX\n", ftell(f)-3);
param[1] = 0x7f;
repairedcmd = 1;
}
lastcode = midicode;
}
break;
case 0xC0:
{
if (!get(f, len, event, n, 1))
goto stoptrack;
if (param[0] >= 0x80)
{
printf( "invalid program number changed at %08lX\n", ftell(f)-2);
param[0] = 0;
repairedcmd = 1;
}
lastcode = midicode;
}
break;
case 0xD0:
{
if (!get(f, len, event, n, 1))
goto stoptrack;
if (param[0] >= 0x80)
{
printf( "invalid aftertouch command changed at %08lX\n", ftell(f)-2);
param[0] = 0x7f;
repairedcmd = 1;
}
lastcode = midicode;
}
break;
case 0xE0:
{
if (!get(f, len, event, n, 2))
goto stoptrack;
lastcode = midicode;
}
break;
default:
{
printf( "removing invalid command %02X at %08lX\n", midicode, ftell(f)-1);
goto stoptrack;
}
}
}
break;
}
if (n > 0)
{
if (fwrite(event, 1, n, of) != n)
{
perror(outputname);
return NOTREPAIRABLE; // disk full
}
n = 0;
}
}
if (endfound && len == 0 && !repairedcmd
#ifdef FIX_CHANNELERROR)
&& !channelerror
#endif
)
return NOTCHANGED;
stoptrack:
len += n;
if (len > 0)
printf( "Warning: %ld bytes lost.\n", len);
if (!endfound)
{
if (fwrite("\0\xff\x2f\0", 1, 4, of) != 4)
{
perror(outputname);
return NOTREPAIRABLE; // disk full
}
return CHANGED;
}
return (!repairedcmd && len == 0) ? NOTCHANGED : CHANGED;
}
int midifix(FILE* f, FILE* of)
{
unsigned char tag[4];
long taglen, tagpos, nextpos, midipos, mtrkpos, endpos, otagpos = 0, otaglen;
int trackcnt, tracks = -1;
int changed = 0;
fseek(f, 0, SEEK_END);
filesize = ftell(f);
fseek(f, 0, SEEK_SET);
tagpos = findmidi(f);
if (tagpos < 0)
{
printf( "%s: No midi data found.\n", name);
return NOTREPAIRABLE;
}
if (tagpos > 0)
changed = 1;
fseek(f, tagpos, SEEK_SET);
if (fread(tag, 1, 4, f) != 4 || !IsHeaderTag(tag))
{
printf( "%s: not a midifile (missing %s)\n", name, mthd);
return NOTREPAIRABLE;
}
fseek(f, tagpos+8, SEEK_SET);
version = getword(f);
trackcnt = getword(f);
fseek(f, tagpos+4, SEEK_SET);
do {
int validtag = (IsHeaderTag(tag) || IsTrackTag(tag));
if (validtag)
{
taglen = getlong(f);
if (taglen < 0)
break;
tracks++; // track0 is assumed header
printf( "%08lX: tag %4.4s found. length=%08lX\n", ftell(f) - 8, tag, taglen);
}
else
fseek(f, -4, SEEK_CUR);
if (IsTag(tag, snio))
{
memcpy(tag, mthd, 4);
changed = 1;
printf("%s: SNio changed to MThd\n", name);
}
int lasttrack = 0;
midipos = findmidi(f);
if (validtag)
endpos = findtrackend(f);
else
endpos = -1;
mtrkpos = findtrack(f);
if (mtrkpos >= 0 && midipos >= 0 && midipos < mtrkpos)
mtrkpos = -1;
if (endpos >= 0 && midipos >= 0 && midipos < endpos)
endpos = -1;
if (!validtag && mtrkpos < 0)
break;
if (validtag && mtrkpos < 0)
lasttrack = 1;
nextpos = mtrkpos;
if (midipos >= 0 && (nextpos > midipos || nextpos < 0))
nextpos = midipos;
if (endpos >= 0 && (nextpos > endpos || nextpos < 0))
nextpos = endpos;
if (nextpos < 0)
{
nextpos = filesize;
lasttrack = 1;
}
if (midipos >= 0 && nextpos == midipos)
lasttrack = 1;
if (validtag)
{
fseek(f, tagpos, SEEK_SET);
if (taglen != nextpos - tagpos - 8)
{
changed = 1;
taglen = nextpos - tagpos - 8;
if (tracks == 0)
printf( "length of header updated\n");
else
printf( "length of track %d updated\n", tracks);
}
otagpos = ftell(of);
if (fwrite(tag, 1, 4, of) != 4) // tag id
return NOTREPAIRABLE;
putlong(of, taglen);
fseek(f, 8, SEEK_CUR);
if (tracks == 0)
copybytes(of, f, taglen);
else
{
int trackchanged = copytrack(of, f, taglen);
if (trackchanged == NOTREPAIRABLE)
return NOTREPAIRABLE;
if (trackchanged == CHANGED)
changed = 1;
}
otaglen = ftell(of) - otagpos - 8;
fseek(of, otagpos+4, SEEK_SET);
putlong(of, otaglen);
fseek(of, otagpos + otaglen + 8, SEEK_SET);
}
else
{
if (lasttrack)
break;
changed = 1;
printf( "remove garbage at end of track (%ld byte%s)\n",
nextpos-ftell(f), nextpos-ftell(f) == 1 ? "" : "s" );
}
fseek(f, nextpos, SEEK_SET);
tagpos = nextpos;
if (lasttrack)
break;
} while (fread(tag, 1, 4, f) == 4);
if (tracks != trackcnt)
{
if (tracks < trackcnt)
printf( "%d track%s missing\n", trackcnt-tracks, trackcnt-tracks==1 ? "" : "s");
else // tracks > trackcnt
printf( "%d new track%s found\n", tracks-trackcnt, tracks-trackcnt==1 ? "" : "s");
updatetrackcount(of, tracks);
changed = 1;
}
int extracount = 0;
while (ftell(f) < filesize)
{
midipos = findmidi(f);
if (midipos < 0)
nextpos = filesize;
else
nextpos = midipos;
if (ftell(f) < nextpos)
{
changed = 1;
printf( "remove garbage at end of midi (%ld byte%s)\n",
nextpos-ftell(f), nextpos-ftell(f) == 1 ? "" : "s" );
}
fseek(f, nextpos, SEEK_SET);
if (midipos < 0)
break;
// MThd found
fgetc(f);
nextpos = findmidi(f);
if (nextpos < 0)
nextpos = filesize;
fseek(f, midipos, SEEK_SET);
// save probably midi as new file
extracount++;
char extraname[14];
sprintf(extraname, extramidi, extracount);
printf( "Extra midi file found. Written to %s\n", extraname);
FILE* ef = fopen(extraname, WRITE_BINARY);
if (!ef)
{
perror(extraname);
return NOTREPAIRABLE;
}
else
{
changed = 1;
copybytes(ef, f, nextpos - midipos);
fclose(ef);
}
}
return changed ? CHANGED : NOTCHANGED;
}
int midifix(char* filename, char* outputname)
{
FILE* f = fopen(filename, READ_BINARY);
if (!f)
{
perror(filename);
return 1;
}
FILE* of = fopen(outputname, WRITE_BINARY);
if (!of)
{
perror(outputname);
return 1;
}
int changed = midifix(f, of);
fclose(f);
fclose(of);
if (changed == NOTREPAIRABLE || changed == NOTCHANGED)
{
remove(outputname);
if (changed == NOTCHANGED)
{
printf( "%s is ok.\n", filename);
return 0;
}
return 1;
}
printf( "output written to %s\n", outputname);
return 0;
}
// checks and fixes chunk table of midi file
int main(int argc, char** argv)
{
argc--; argv++;
while (argc > 0 && **argv == '-')
{
if (strncmp(*argv, "-tracksonly", 2) == 0)
{
tracksonly = 1;
argc--; argv++; continue;
}
fprintf(stderr, "Invalid option %s.\n", *argv);
argc--; argv++;
}
if (!argc)
{
printf( "usage: midifix [-tracksonly] filename.mid [result.mid]\n");
printf("-tracksonly\tcheck and repair only track headers\n");
printf( "if output filename is not given and changes are necessary then\n");
printf( "output will be written to midifix.mid\n");
return 1;
}
name = *argv++; argc--;
if (argc > 0)
outputname = *argv;
else
outputname = DEFAULT_OUTPUTNAME;
if (STRICMP(name, outputname) == 0)
{
if (STRICMP(name, DEFAULT_OUTPUTNAME) == 0)
{
printf( "Cannot write to %s. Must specify alternate outputname!\n",
DEFAULT_OUTPUTNAME);
return 1;
}
printf( "Cannot change input file. Write output to %s!\n", outputname);
outputname = DEFAULT_OUTPUTNAME;
}
return midifix(name, outputname);
}